/**
 * @file tuya_os_adapt_semaphore.c
 * @brief semaphore相关接口封装
 * 
 * @copyright Copyright(C),2018-2020, 涂鸦科技 www.tuya.com
 * 
 */

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

#include "bull_hal_sys_semaphore.h"
#include "bull_hal_sys_memory.h"

#define portYIELD_FROM_ISR_ex( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()

/***********************************************************
*************************micro define***********************
***********************************************************/
typedef struct
{
    xSemaphoreHandle sem;
}SEM_MANAGE, *P_SEM_MANAGE;


/***********************************************************
*************************variable define********************
***********************************************************/


/***********************************************************
*************************function define********************
***********************************************************/

static SEM_HANDLE CreateSemaphore(void)
{
    P_SEM_MANAGE pSemManage;
    
    pSemManage = (P_SEM_MANAGE)bull_hal_sys_memory_malloc(sizeof(SEM_MANAGE));

    return (SEM_HANDLE)pSemManage;
}


static int InitSemaphore(const SEM_HANDLE semHandle, const unsigned int semCnt,\
                         const unsigned int sem_max)
{
    if(!semHandle)
        return OPRT_OS_ADAPTER_INVALID_PARM;
        
    P_SEM_MANAGE pSemManage;
    pSemManage = (P_SEM_MANAGE)semHandle;

    pSemManage->sem = xSemaphoreCreateCounting(sem_max, semCnt);
    if(NULL == pSemManage->sem) {
        return OPRT_OS_ADAPTER_SEM_CREAT_FAILED;
    }

    return OPRT_OS_ADAPTER_OK;
}

/**
 * @brief  用于创建并初始化semaphore
 * 
 * @param[out] *semHandle semaphore句柄
 * @param[in] init_semCnt  创建信号量时分配给信号量的计数值
 * @param[in] sem_max 可以达到的最大计数值
 * @return int 0=成功，非0=失败
 */
int bull_hal_sys_semaphore_create(SEM_HANDLE *semHandle, const uint32_t init_semCnt, const uint32_t sem_max)
{
    if(NULL == semHandle) {
        return OPRT_OS_ADAPTER_INVALID_PARM;
    }

    *semHandle = CreateSemaphore();
    if(*semHandle == NULL) {
        return OPRT_OS_ADAPTER_MALLOC_FAILED;
    }

    int ret = InitSemaphore(*semHandle, init_semCnt, sem_max);
    if(ret != OPRT_OS_ADAPTER_OK) {
        bull_hal_sys_memory_free(*semHandle);
        *semHandle = NULL;
        return ret;
    }

    return OPRT_OS_ADAPTER_OK;
}

/**
 * @brief bull_hal_sys_semaphore_wait 用于wait semaphore
 * 
 * @param[in] semHandle semaphore句柄
 * @return int 0=成功，非0=失败
 */
int bull_hal_sys_semaphore_wait( IN const SEM_HANDLE semHandle)
{
    if(!semHandle) {
        return OPRT_OS_ADAPTER_INVALID_PARM;
    }

    P_SEM_MANAGE pSemManage;
    pSemManage = (P_SEM_MANAGE)semHandle;

    BaseType_t ret;
    ret = xSemaphoreTake(pSemManage->sem, portMAX_DELAY);
    if(pdTRUE != ret) {
        return OPRT_OS_ADAPTER_SEM_WAIT_FAILED;
    }

    return OPRT_OS_ADAPTER_OK;
}

/**
 * @brief bull_hal_sys_semaphore_waittimeout used fo wait semaphore with timeout
 *
 * @param[in] semHandle semaphore句柄
 * @param[in] timeout  semaphore超时时间
 * @return int 0=成功，非0=失败
*/
int bull_hal_sys_semaphore_wait_timeout(IN const SEM_HANDLE semHandle, uint32_t timeout)
{
    if(!semHandle) {
        return OPRT_OS_ADAPTER_INVALID_PARM;
    }

    P_SEM_MANAGE pSemManage;
    pSemManage = (P_SEM_MANAGE)semHandle;

    BaseType_t ret;
    if (timeout == BULL_HAL_SYS_SEM_FOREVER) {
        ret = xSemaphoreTake(pSemManage->sem, portMAX_DELAY);
    } else {
        ret = xSemaphoreTake(pSemManage->sem, timeout / bull_hal_sys_get_tickratems());
    }
    if (pdTRUE != ret) {
        return OPRT_OS_ADAPTER_SEM_WAIT_FAILED;
    }

    return OPRT_OS_ADAPTER_OK;
}

/**
 * @brief bull_hal_sys_semaphore_post用于post semaphore
 * 
 * @param[in] semHandle semaphore句柄
 * @return int 0=成功，非0=失败
 */
int bull_hal_sys_semaphore_post( IN const SEM_HANDLE semHandle)
{
    if(!semHandle) {
        return OPRT_OS_ADAPTER_INVALID_PARM;
    }

    P_SEM_MANAGE pSemManage;
    pSemManage = (P_SEM_MANAGE)semHandle;

    BaseType_t ret;

    // if(FALSE == bull_hal_sys_is_in_isr()) {
    //     ret = xSemaphoreGive(pSemManage->sem);
    // }else {
         signed portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
    //     ret = xSemaphoreGiveFromISR(pSemManage->sem,&xHigherPriorityTaskWoken);
    //     // portYIELD_FROM_ISR_ex(xHigherPriorityTaskWoken);
    // }
    ret = xSemaphoreGiveFromISR(pSemManage->sem,&xHigherPriorityTaskWoken);
    if(pdTRUE != ret) {
        return OPRT_OS_ADAPTER_SEM_POST_FAILED;
    }

    return OPRT_OS_ADAPTER_OK;
}

/**
 * @brief bull_hal_sys_semaphore_release 用于release semaphore
 * 
 * @param[in] semHandle 
 * @return int 0=成功，非0=失败
 */
int bull_hal_sys_semaphore_delete( IN const SEM_HANDLE semHandle)
{
    if(!semHandle) {
        return OPRT_OS_ADAPTER_INVALID_PARM;
    }

    P_SEM_MANAGE pSemManage;
    pSemManage = (P_SEM_MANAGE)semHandle;

    vSemaphoreDelete(pSemManage->sem);
    bull_hal_sys_memory_free(semHandle); // 释放信号量管理结构

    return OPRT_OS_ADAPTER_OK;
}

